Convolutions have become very popular in recent years, especially in the area of image processing. This is due to their ability to handle raw data from sensors. There is a lot of information available regarding neural nets involving 2D convolutions but not so much when it comes to 1D convolutions. It is my hope to contribute to that gap in this notebook/article.
In a normal matrix operation, a map M is applied on input data x. This is shown in the image below.
On the left you see a big box of height m and width n, this represent the matrix Map, on the right you see the input data which has the same height as the map. When the map is applied on the input, it projects it according to the information encoded in the map
The convolution operation is similar to a normal matrix operation in a sense that it is also a kind of projection of the input data onto a differnt representation. Here, instead of a matrix map, kernels are used to create the projection.
On the left you see the kernles stacked on eachother (given in different colors), on the right you see the result of convolution.
The kernel can also be applied on a multi-channel data like in the case of a stereo input. This is shown in the image below.
# Import the usual suspects
import torch
from torch import nn
import numpy as np
# 16 channels
# 2 channels
# 3 length
conv = nn.Conv1d(in_channels=2, out_channels=16, kernel_size=3)
print('Architecture\n\t',conv)
print('\nWeights\n\t', conv.weight.size())
print('\nBias\n\t',conv.bias.size())
# 1 bias per channel so 16
Note that whenever a signal is convolved, the length of the data will reduce by the kernel number minus one
The data needs to be padded such that the data doesn't shrink too much
# Generate random data
# 1 signal, 2 channels, 64 samples
x = torch.randn(1, 2, 64)
print('Bias: ', conv.bias.size())
print('Output: ', conv(x).size())
# Lets use a 2x2 tensor with gradient
x = torch.tensor([[1, 2], [3, 4]], requires_grad=True, dtype=torch.float32)
print(x)
# perform some operation
y = x - 2
print('Gradient function: ',y.grad_fn)
print()
print(y.grad_fn.next_functions[0][0])
print(y.grad_fn.next_functions[0][0].variable) # the original tensor
# Do another operation on y
z = y * y * 3
a = z.mean()
print(z)
print(a)
from torchviz import make_dot
make_dot(a)
This computes gradients to be used for training neural nets with gradient descent
The gradient is manually computed below:
# compute gradients by using backward()
a.backward()
print(x)
print('\nGradient:')
print(x.grad)